昨天角色的 CRUD 功能都完成了,接著就是要把角色指派給使用者了,先建立一個 ViewModel CustomUserRoleViewModel,這是用來呈現角色底下使用者的 ViewModel。
namespace BlazorServer.ViewModels
{
    public class CustomUserRoleViewModel
    {
        public string UserId { get; set; }
        public string UserName { get; set; }
        public bool IsSelected { get; set; }
    }
}
再於IRolesRepository跟RolesRepository加上編輯角色內使用者的功能,這裡用了多載,第一個相當於 Get 取得畫面初始資料,第二個則是 Post 送出修改後的資料。IRolesRepository.cs
…
        Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string RoleId);
        Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string RoleId);
RolesRepository.cs
…
        public async Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string RoleId)
        {
            var role = await _roleManager.FindByIdAsync(RoleId);
            var model = new List<CustomUserRoleViewModel>();
            foreach (var user in _userManager.Users)
            {
                var userRoleViewModel = new CustomUserRoleViewModel
                {
                    UserId = user.Id,
                    UserName = user.UserName
                };
                if (await _userManager.IsInRoleAsync(user, role.Name))
                {
                    userRoleViewModel.IsSelected = true;
                }
                else
                {
                    userRoleViewModel.IsSelected = false;
                }
                model.Add(userRoleViewModel);
            }
            return model;
        }
        public async Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string RoleId)
        {
            var role = await _roleManager.FindByIdAsync(RoleId);
            foreach (var m in model)
            {
                var user = await _userManager.FindByIdAsync(m.UserId);
                IdentityResult result;
                if (m.IsSelected && !(await _userManager.IsInRoleAsync(user, role.Name)))
                {
                    result = await _userManager.AddToRoleAsync(user, role.Name);
                }
                else if (!m.IsSelected && await _userManager.IsInRoleAsync(user, role.Name))
                {
                    result = await _userManager.RemoveFromRoleAsync(user, role.Name);
                }
                else
                {
                    continue;
                }
                if (result.Succeeded)
                {
                    if (model.Count > 0)
                    {
                        continue;
                    }
                    else
                    {
                        return new ResultViewModel
                        {
                            Message = RoleId,
                            IsSuccess = true
                        };
                    }
                }
                else
                {
                    return new ResultViewModel
                    {
                        Message = RoleId,
                        IsSuccess = false
                    };
                }
            }
            return new ResultViewModel
            {
                Message = RoleId,
                IsSuccess = true
            };
        }
接著加上畫面。EditUsersInRole.razor.cs
using BlazorServer.Services;
using BlazorServer.Shared;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlazorServer.Pages.RolesManagement
{
    public partial class EditUsersInRole
    {
        [Inject] protected IRolesRepository RolesRepository { get; set; }
        [Inject] protected NavigationManager NavigationManager { get; set; }
        [Inject] protected IJSRuntime js { get; set; }
        private JsInteropClasses jsClass;
        [Parameter]
        public string RoleId { get; set; }
        public List<CustomUserRoleViewModel> UserRoleViewModel { get; set; } = new List<CustomUserRoleViewModel>();
        protected override async Task OnInitializedAsync()
        {
            await loadData();
            jsClass = new(js);
        }
        private async Task loadData()
        {
            UserRoleViewModel = (await RolesRepository.EditUsersInRoleAsync(RoleId)).ToList();
        }
        public async Task HandleValidSubmit()
        {
            var result = await RolesRepository.EditUsersInRoleAsync(UserRoleViewModel, RoleId);
            if (result.IsSuccess)
            {
                NavigationManager.NavigateTo($"/RolesManagement/EditRole/{RoleId}");
            }
            else
            {
                await jsClass.Alert(result.Message);
            }
        }
        public void Cancel()
        {
            NavigationManager.NavigateTo($"/RolesManagement/EditRole/{RoleId}");
        }
    }
}
EditUsersInRole.razor
@attribute [Authorize]
<EditForm Model="UserRoleViewModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div class="card">
        <div class="card-header">
            <h2>從角色新增或移除使用者</h2>
        </div>
        <div class="card-body">
            @foreach (var user in UserRoleViewModel)
            {
                <div class="form-check m-1">
                    <label class="form-check-label">
                        <InputCheckbox @bind-Value="@user.IsSelected"></InputCheckbox>
                        @user.UserName
                    </label>
                </div>
            }
        </div>
        <div class="card-footer">
            <button type="submit" class="btn btn-primary">更新</button>
            <button type="button" class="btn btn-danger" @onclick="@Cancel">取消</button>
        </div>
    </div>
</EditForm>
EditRole.razor.cs新增一個方法,通往EditUsersInRole.razor,可以編輯角色底下使用者。
        public void EditUsersInRole()
        {
            NavigationManager.NavigateTo($"/RolesManagement/EditUsersInRole/{RoleId}");
        }
EditRole.razor加上一個按鈕呼叫該方法。
            <button type="button" class="btn btn-info" @onclick="EditUsersInRole">新增或移除該角色底下的使用者</button>

啟動網頁進入編輯角色下使用者頁面,可以看到目前只有一個使用者,將Admin角色指派給這個使用者,如此一來就完成了Admin跟test@gmail.com的指派,不過要測試是否成功的話,需要對頁面加入限制,還要新增一個使用者測試。
先將每個 razor component 的@attribute [Authorize]改成@attribute [Authorize(Roles = "Admin")],表示要看到這頁面的人必須有 Role Admin。
再去NavMenu.razor,於原本的<AuthorizeView>上方加入下面這段程式,同理要看到通往Roles連結的人必須有 Role Admin,再把原本<AuthorizeView>的Roles連結刪除。
        <AuthorizeView Roles="Admin">
            <Authorized>
                <li class="nav-item px-3">
                    <NavLink class="nav-link" href="RolesManagement/RolesList" Match="NavLinkMatch.All">
                        <span class="bi bi-kanban-fill h4 p-2 mb-0" aria-hidden="true"></span> Roles
                    </NavLink>
                </li>
            </Authorized>
        </AuthorizeView>
最後新增一名使用者,名為user@gmail.com,以此使用者登入,會發現左邊的Roles不見了,手動輸入網址也會提示沒有權限,這就是最基本的角色(Role)授權,如果系統很簡單只要用角色劃分權限,這樣就能滿足需求了。

Ref:Add or remove users from role in asp net core